package cn.monkey.chat.server.admin.domain.server;

import cn.monkey.chat.server.data.Result;
import cn.monkey.chat.server.data.Results;
import cn.monkey.chat.server.data.server.ServerVo;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.NoSuchElementException;

@Service
public class ServerService implements IServerService {

    private final ServerRepository serverRepository;
    private final ServerLoadRepository serverLoadRepository;

    public ServerService(ServerRepository serverRepository, ServerLoadRepository serverLoadRepository) {
        this.serverRepository = serverRepository;
        this.serverLoadRepository = serverLoadRepository;
    }

    static long boxedLongToLong(Long aLong) {
        return aLong == null ? 0L : aLong;
    }

    @Override
    public Mono<Result<ServerVo>> findBestServer() {
        return this.serverRepository.findAll()
                .flatMap(server -> this.serverLoadRepository.findById(server.getId())
                        .map(serverLoad -> {
                            ServerVo serverVo = new ServerVo();
                            serverVo.setUrl(server.getUrl());
                            serverVo.setCurrentLoad(serverLoad.getCurrentLoad());
                            serverVo.setMaxLoad(serverLoad.getMaxLoad());
                            return serverVo;
                        }))
                .collectList()
                .flatMap(servers -> {
                    servers.sort((s1, s2) -> {
                        long s1Release = boxedLongToLong(s1.getMaxLoad()) - boxedLongToLong(s1.getCurrentLoad());
                        long s2Release = boxedLongToLong(s2.getMaxLoad()) - boxedLongToLong(s2.getCurrentLoad());
                        return Math.toIntExact(s2Release - s1Release);
                    });
                    try {
                        return Mono.just(servers.getFirst());
                    } catch (NoSuchElementException e) {
                        return Mono.error(e);
                    }
                })
                .map(Results::ok)
                .onErrorResume(e -> Mono.just(Results.error(e)));
    }
}
